﻿using System;
using System.Collections.Generic;
using System.Windows.Media;

namespace Red.Toolkit.Profiler.Logic
{
    public class Graph
    {
        public delegate void GraphCreationDelegate( Graph graph );

        private bool _visible;
        private ValueType _type;
        protected string _name;
        protected SortedDictionary<double, double> _values;
        private GraphNode _rootNode;

        private GraphCreationDelegate _onCreate;
        private bool _created;

        // min/max values
        private double _min;
        private double _max;

        private Color _color;
        private Brush _brush;
        private Pen _pen;

        public Graph( string name, ValueType type, GraphCreationDelegate creationDelegate )
        {
            _visible = false;
            _type = type;
            _name = name;
            _rootNode = null;
            _min = _max = 0.0;
            _values = new SortedDictionary<double, double>();

            _color = Utils.RandomColor( 0.2, 1.0, 0.5, 1.0 );
            _brush = new SolidColorBrush( _color );
            _pen = new Pen( _brush, 1.5 );
            _brush.Freeze();
            _pen.Freeze();

            _created = false;
            _onCreate = creationDelegate;
        }

        public string Name { get { return _name; } }
        public ValueType Type { get { return _type; } }
        public GraphNode RootNode { get { return _rootNode; } }
        public Color Color { get { return _color; } }
        public Brush Brush { get { return _brush; } }
        public Pen Pen { get { return _pen; } }

        private void Prepare()
        {
            if ( _values.Count < 1 )
                return;

            _min = double.MaxValue;
            _max = double.MinValue;

            GraphNode[] prevLayer = new GraphNode[_values.Count];
            int id = 0;
            foreach ( var p in _values )
            {
                double v = p.Value;
                prevLayer[id++] = new GraphNode( p.Key, v );
                _min = Math.Min( _min, v );
                _max = Math.Max( _max, v );
            }

            GraphNode[] newLayer = prevLayer;
            while ( prevLayer.Length > 1 )
            {
                bool odd = prevLayer.Length % 2 == 1;
                int newLayerCount = prevLayer.Length / 2;
                if ( odd )
                    newLayerCount++;

                newLayer = new GraphNode[newLayerCount];
                for ( int i = 0; i < prevLayer.Length / 2; ++i )
                    newLayer[i] = new GraphNode( prevLayer[2 * i], prevLayer[2 * i + 1] );

                if ( odd )
                    newLayer[prevLayer.Length / 2] = prevLayer[prevLayer.Length - 1];

                prevLayer = newLayer;
            }

            _rootNode = newLayer[0];
        }

        public void AddPoint( double x, double y )
        {
            if ( !_values.ContainsKey( x ) )
                _values.Add( x, y );
        }

        public bool Visible
        {
            get { return _visible; }
            set
            {
                _visible = value;
                if ( _visible )
                    Create();
            }
        }

        public double Min { get { return _min; } }
        public double Max { get { return _max; } }
        public SortedDictionary<double, double> Values { get { return _values; } }

        public double GetValueFor( double time )
        {
            GraphNode node = _rootNode;

            if ( node == null )
                return -1.0;

            while ( node.Left != null )
            {
                if ( time >= node.Split )
                    node = node.Right;
                else
                    node = node.Left;
            }

            return node.value;
        }

        public void Create()
        {
            if ( _created )
                return;

            _onCreate( this );
            Prepare();
            _created = true;
        }
    }
}
